package idv.ccApp.cwFileExplore;

import android.app.AlertDialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.*;

import org.apache.commons.io.FileSystemUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

import android.util.Log;
import android.view.*;
import android.widget.*;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class cwFileExploreActivity extends ListActivity {
  /**
   * Called when the activity is first created.
   */
  final private static String TAG = "cwFEA";

  //////////////////////
  // version 1
  private String mRootPath = "/";

  private String COL_ICON = "col_icon";
  private String COL_PATH = "col_path";
  private String COL_ABSPATH = "col_abspath";
  private String COL_MIMETYPE = "col_mimetype";
  private String COL_CHECKBOX = "col_checkbox";

  private String[] mColumnNames = {COL_ICON, COL_PATH, COL_CHECKBOX, COL_ABSPATH, COL_MIMETYPE};

  //private int[] mListRowLocations = {R.id.id_imgIcon, R.id.id_path_item, R.id.id_file_select_checkBox};
  private int[] mListRowLocations = {R.id.id_imgIcon, R.id.id_path_item};

  Parcelable mListViewState = null;

  private List<HashMap<String, Object>> mListMap = null;
  private SimpleAdapter mSimpleAdapter = null;
  //private CheckBoxSimpleAdater mSimpleAdapter = null;id_file_select_checkBox

  private ProgressDialog mFopProgressDlg = null;
  private Button mPasteButton = null;
  private ListView   mFileListView = null;

  private String mTitlePath = "";
  private String mSingleFileFromItem = "";
  private String mSingleFileToItem = "";
  public  enum  ENUM_MSG_TYPE {
    MSG_TYPE_COPY_START,
    MSG_TYPE_COPY_FINISH,
    MSG_TYPE_DELETE_START,
    MSG_TYPE_DELETE_FINISH,
    MSG_TYPE_NONE
  }

  public class Runnable_Fop_Delete implements Runnable {
    private String mDeletePath;
    public Runnable_Fop_Delete(String path) {
      this.mDeletePath = path;
    }
    public void run() {
      File file = new File(this.mDeletePath);
      FileUtils.deleteQuietly(file);
      Message m = new Message();
      m.what = ENUM_MSG_TYPE.MSG_TYPE_DELETE_FINISH.ordinal();
      m.obj = this.mDeletePath;
      mUI_Delete_Handler.sendMessage(m);
    }
  }
  public class Runnable_Fop_Copy implements Runnable {
    private String mSrc;
    private String mDst;
    private boolean mIsDirectory;
    public Runnable_Fop_Copy(String src, String dst, boolean isDirectory) {
      this.mSrc = src;
      this.mDst = dst;
      this.mIsDirectory = isDirectory;
    }
    public void run() {
      final File srcFile = new File(this.mSrc);
      final File desFile = new File(this.mDst);
      Log.d(TAG, "Copy from:" + this.mSrc + " to:" + this.mDst);
      try{
        if(this.mIsDirectory)
          FileUtils.copyDirectory(srcFile, desFile);
        else
          FileUtils.copyFile(srcFile, desFile);
      } catch ( IOException e) {
        Log.e(TAG, getResources().getString(R.string.sentence_copy_error));
      }
      Message m = new Message();
      m.what = ENUM_MSG_TYPE.MSG_TYPE_COPY_FINISH.ordinal();
      m.obj = this.mDst;
      mUI_Copy_Handler.sendMessage(m);
      //mUI_Handler.sendMessage(m);
    }
  }
  private Comparator<? super File> mFileSortComparator_ignore_case = new Comparator<File>() {
    @Override
    public int compare(File lhs, File rhs) {
      if (lhs.isDirectory() && rhs.isDirectory())
        return String.valueOf(lhs.getName().toLowerCase()).compareTo(rhs.getName().toLowerCase());
      if (lhs.isDirectory() && !rhs.isDirectory())
        return -1;
      if (!lhs.isDirectory() && rhs.isDirectory())
        return 1;
      if (!lhs.isDirectory() && !rhs.isDirectory())
        return String.valueOf(lhs.getName().toLowerCase()).compareTo(rhs.getName().toLowerCase());
      return 0;
    }
  };
  private void lockListViewUI (){
    this.mFileListView.setFocusable(false);
    this.mFileListView.setClickable(false);
    this.mFileListView.setEnabled(false);
  }
  private void unlockListViewUI(){
    this.mFileListView.setFocusable(true);
    this.mFileListView.setClickable(true);
    this.mFileListView.setEnabled(true);
  }
  private Handler mUI_Copy_Handler = new Handler(){
    @Override
    public void handleMessage(Message msg){
      ENUM_MSG_TYPE which = ENUM_MSG_TYPE.values()[msg.what];
      final ProgressDialog pd = mFopProgressDlg;
      final String repath = mTitlePath;
      switch(which){
        case MSG_TYPE_COPY_START:{
          mListViewState = mFileListView.onSaveInstanceState();
          pd.setTitle(getResources().getString(R.string.str_File)
              + " "
              + getResources().getString(R.string.str_Copy));
          pd.setMessage(getResources().getString(R.string.sentence_copying_message));
          lockListViewUI();
          pd.show();
        } break;
        case MSG_TYPE_COPY_FINISH:{
          addItemToList((String)msg.obj);
          loadFileList(repath);
          pd.dismiss();
          unlockListViewUI();
          mFileListView.onRestoreInstanceState(mListViewState);
          resetSavedInfo();
        } break;
      }
    }
  };
  private Handler mUI_Delete_Handler = new Handler(){
    @Override
    public void handleMessage(Message msg){
      ENUM_MSG_TYPE which = ENUM_MSG_TYPE.values()[msg.what];
      final ProgressDialog pd = mFopProgressDlg;
      final String reloadPath = mTitlePath;
      switch(which){
        case MSG_TYPE_DELETE_START:{
          mListViewState = mFileListView.onSaveInstanceState();
          pd.setTitle( getResources().getString(R.string.str_File)
              + " "
              + getResources().getString(R.string.str_Delete));
          pd.setMessage(getResources().getString(R.string.sentence_deleting_message));
          pd.show();
          lockListViewUI();
        } break;
        case MSG_TYPE_DELETE_FINISH:{
          loadFileList(reloadPath);
          pd.dismiss();
          unlockListViewUI();
          mFileListView.onRestoreInstanceState(mListViewState);
          resetSavedInfo();
        } break;
      }
    }
  };
  private boolean handleContextItemCopyClicked() {
    boolean ret = true;
    // default disable
    this.mPasteButton.setVisibility(View.VISIBLE);
    return ret;
  }
  private boolean handleContextItemDeleteClicked(final String abspath){
    boolean ret = true;

    myUtilClass.MyTRACE("abspath is " + abspath);

    DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog, int which) {
        switch (which){
          case DialogInterface.BUTTON_POSITIVE:{
            Message m = new Message();
            m.what = ENUM_MSG_TYPE.MSG_TYPE_DELETE_START.ordinal();
            m.obj = abspath;
            mUI_Delete_Handler.sendMessage(m);

            Runnable_Fop_Delete r1 = new Runnable_Fop_Delete(abspath);
            Thread t1 = new Thread(r1);
            t1.start();
          } break;

          default:
          case DialogInterface.BUTTON_NEGATIVE:{
          } break;
        }
      }
    };
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage(R.string.title_confirm_to_delete_file_folder).setPositiveButton(R.string.str_yes, dialogClickListener)
      .setNegativeButton(R.string.str_No, dialogClickListener).show();

    return ret;
  }
  @Override
  public boolean onContextItemSelected(MenuItem item) {
    boolean ret = false;
    AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
    HashMap<String, Object>map =  (HashMap<String, Object>)getListAdapter().getItem(info.position);

    switch (item.getItemId()) {
      case R.id.id_menu_item_copy: {
        this.mSingleFileFromItem = map.get(this.COL_ABSPATH).toString();
        myUtilClass.MyTRACE("this.mFileFromItem:" + this.mSingleFileFromItem);
        ret = this.handleContextItemCopyClicked();
      } break;

      case R.id.id_menu_item_delete:{
        ret = this.handleContextItemDeleteClicked(map.get(this.COL_ABSPATH).toString());
      } break;

      default:
        return super.onContextItemSelected(item);
    }
    return ret;
  }
  private void loadXmlForContextMenu(ContextMenu menu) {
    MenuInflater inflater = this.getMenuInflater();
    inflater.inflate(R.menu.file_op_popup, menu);
  }
  @Override
  public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    myUtilClass.MyTRACE();
    super.onCreateContextMenu(menu, v, menuInfo);
    this.loadXmlForContextMenu(menu);
  }
  @Override
  public boolean onPrepareOptionsMenu(Menu menu) {
    return super.onPrepareOptionsMenu(menu);
  }
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    String abs_path;

    do {
      if (this.isExternalStorageMounted()) {
        abs_path = Environment.getExternalStorageDirectory().getAbsolutePath();
        break;
      }
      abs_path = Environment.getDataDirectory().getAbsolutePath();
    } while (false);
    this.loadFileList(abs_path);
    this.registerForContextMenu(this.getListView());

    this.mFileListView = this.getListView();

    this.mFopProgressDlg = new ProgressDialog(this);
    this.mFopProgressDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    this.mFopProgressDlg.setCancelable(false);
    //this.mFopProgressDlg.setIcon(R.drawable.img1);
    //this.mFopProgressDlg.setIndeterminate(false);

    // default disable
    this.mPasteButton = (Button) this.findViewById(R.id.id_PasteButton);
    this.mPasteButton.setVisibility(View.GONE);
    this.mPasteButton.setOnClickListener( this.mPasteClicked);

  }

  private void resetSavedInfo(){
    this.mSingleFileFromItem = "";
    this.mSingleFileToItem = "";
    this.mListViewState = null;
  }
  private void addItemToList(String absPath){
    HashMap<String,Object>map = new HashMap<String,Object>();
    this.fileTypeClassify(map, new File(absPath));
    this.mListMap.add(map);
  }
  private String getSingleFileCopyFromItem() {
    return this.mSingleFileFromItem;
  }
  private String getSingleFileCopyToItem(){
    return this.mSingleFileToItem;
  }
  private String buildNewNameForDuplicateDir(String orgAbsoluteFileName){
    boolean found = false;
    int i=0;
    String orgFillNameNoEndSeparator = FilenameUtils.getFullPathNoEndSeparator(orgAbsoluteFileName);
    String new_dir_name;

    do {
      i++;
      new_dir_name = orgFillNameNoEndSeparator + "(" + Integer.toString(i)  + ")" + "/";
      File newDir = new File(new_dir_name);
      if (!newDir.exists()){
        found = true;
      }
    } while(!found);
    myUtilClass.MyTRACE("new dir name selected:" + new_dir_name);
    return new_dir_name;
  }
  private String buildNewNameForDuplicateFile(String orgAbsoluteFileName){
    boolean found = false;
    int i=0;
    String orgBaseName = FilenameUtils.getBaseName(orgAbsoluteFileName);
    String new_file_name;

    do {
      i++;
      new_file_name = FilenameUtils.getFullPath(orgAbsoluteFileName)
        + orgBaseName
        + "(" + Integer.toString(i)  + ")"
        + "."
        + FilenameUtils.getExtension(orgAbsoluteFileName);
      File newFile = new File(new_file_name);
      if (!newFile.exists()){
        found = true;
      }
    } while(!found);
    myUtilClass.MyTRACE("new file name selected:" + new_file_name);
    return new_file_name;
  }
  private View.OnClickListener mPasteClicked = new View.OnClickListener() {
    public void onClick(View v) {
      final Handler pH = mUI_Copy_Handler;//mUI_Handler;
      String fromPath = getSingleFileCopyFromItem();
      String toPath = getSingleFileCopyToItem();
      mPasteButton.setVisibility(View.GONE);
      do {
        File fromFile = new File(fromPath);
        if(fromPath.equals("")) {
          Toast.makeText(getApplicationContext(), R.string.sentence_err_no_source_file_selected, Toast.LENGTH_LONG).show();
          break;
        }
        if(toPath.equals("")){
          if (fromFile.isDirectory()){
            toPath = buildNewNameForDuplicateDir(fromPath);
          } else {
            toPath = buildNewNameForDuplicateFile(fromPath);
          }
        }
        Message m = new Message();
        m.what = ENUM_MSG_TYPE.MSG_TYPE_COPY_START.ordinal();
        pH.sendMessage(m);
        Runnable_Fop_Copy r1 = new Runnable_Fop_Copy(fromPath, toPath, fromFile.isDirectory());
        Thread t1 = new Thread(r1);
        t1.start();
      } while(false);
    }
  };

  @Override
  protected void onListItemClick(ListView l, View v, int position, long id) {
    super.onListItemClick(l, v, position, id);
    if (!l.isClickable()){
      myUtilClass.MyTRACE("xxaddd");
      return;
    }

    HashMap<String, Object> map = this.mListMap.get(position);
    String path = map.get(this.COL_PATH).toString();
    String abs_path = map.get(this.COL_ABSPATH).toString();
    String mime_type = map.get(this.COL_MIMETYPE).toString();

    myUtilClass.MyTRACE("path=" + path + ",abs_path=" + abs_path + ",mime=" + mime_type);

    if (mime_type.equals("")) {
      String reload_path = abs_path;
      if (path.equals("../")) {
        myUtilClass.MyTRACE("parent dir click: path=" + abs_path);
        File f = new File(abs_path);
        reload_path = f.getParent();
      }
      myUtilClass.MyTRACE("reload_path is " + reload_path);
      this.loadFileList(reload_path);

      if ( !this.mSingleFileFromItem.equals("")) {
        this.mSingleFileToItem = reload_path;
      }
      return;
    }
    File item = new File(abs_path);
    this.startDefaultActivityForFile(map, item);
  }
  private void choiceFileTypeIconAndMime(HashMap<String, Object> map, File file) {

    String unknown_mime_type = "application/*";

    String pdf_mime_type = "application/pdf";
    String pdf_file_types[] = {".pdf"};

    String apk_mime_type = "application/vnd.android.package-archive";
    String apk_file_types[] = {".apk"};

    String image_mime_type = "image/*";
    String image_file_types[] = {"jpg", "bmp", "png"};

    String audio_mime_type = "audio/*";
    String audio_file_types[] = {".mp3", ".wav", ".wma"};

    String video_mime_type = "video/*";
    String video_file_types[] = {".mov", ".mp4", ".avi", ".wmv"};

    String text_mime_type = "text/*";
    String text_file_types[] = {".rc", ".sh", "ini", ".log", ".txt"};

    boolean file_type_found = false;
    Object icon_object = R.drawable.ic_filetype_unknown;
    String str_mime_type = unknown_mime_type;
    String name = file.getName();

    do {

      for (int i = 0; i < apk_file_types.length; i++) {
        if (name.toLowerCase().endsWith(apk_file_types[i])) {
          icon_object = R.drawable.ic_filetype_apk;
          str_mime_type = apk_mime_type;
          file_type_found = true;
          break;
        }
      }
      if (file_type_found)
        break;

      for (int i = 0; i < pdf_file_types.length; i++) {
        if (name.toLowerCase().endsWith(pdf_file_types[i])) {
          icon_object = R.drawable.ic_filetype_text;
          str_mime_type = pdf_mime_type;
          file_type_found = true;
          break;
        }
      }
      if (file_type_found)
        break;

      for (int i = 0; i < text_file_types.length; i++) {
        if (name.toLowerCase().endsWith(text_file_types[i])) {
          icon_object = R.drawable.ic_filetype_text;
          str_mime_type = text_mime_type;
          file_type_found = true;
          break;
        }
      }
      if (file_type_found)
        break;

      for (int i = 0; i < audio_file_types.length; i++) {
        if (name.toLowerCase().endsWith(audio_file_types[i])) {
          icon_object = R.drawable.ic_filetype_audio;
          str_mime_type = audio_mime_type;
          file_type_found = true;
        }
      }
      if (file_type_found)
        break;

      for (int i = 0; i < video_file_types.length; i++) {
        if (name.toLowerCase().endsWith(video_file_types[i])) {
          icon_object = R.drawable.ic_filetype_video;
          str_mime_type = video_mime_type;
          file_type_found = true;
        }
      }
      if (file_type_found)
        break;

      for (int i = 0; i < image_file_types.length; i++) {
        if (name.toLowerCase().endsWith(image_file_types[i])) {
          icon_object = R.drawable.ic_filetype_image;
          str_mime_type = image_mime_type;
          file_type_found = true;
          break;
        }
      }
      if (file_type_found)
        break;

    } while (false);
    map.put(this.COL_ICON, icon_object);
    map.put(this.COL_MIMETYPE, str_mime_type);
  }
  private void fileTypeClassify(HashMap<String, Object> map, File file) {

    if (file.isDirectory()) {
      map.put(this.COL_ICON, R.drawable.ic_folder);
      map.put(this.COL_MIMETYPE, "");
      map.put(this.COL_ABSPATH, file.getAbsolutePath() + "/");
      map.put(this.COL_CHECKBOX, false);
    } else {
      this.choiceFileTypeIconAndMime(map, file);
      map.put(this.COL_ABSPATH, file.getAbsolutePath());
      map.put(this.COL_CHECKBOX, false);
    }
    map.put(this.COL_PATH, file.getName());
  }
  private void loadFileList(String input_path) {
    if (this.mListMap != null) {
      for (HashMap<String, Object> map : this.mListMap) {
        map.clear();
      }
      this.mListMap.clear();
      this.mListMap = null;
    }
    this.mListMap = new ArrayList<HashMap<String, Object>>();
    myUtilClass.MyTRACE("renew mListMap for path=" + input_path);
    File f = new File(input_path);
    File[] files = f.listFiles();
    this.setTitle(input_path);
    this.mTitlePath = input_path;
    if (!input_path.equals(this.mRootPath)) {
      HashMap<String, Object> map = new HashMap<String, Object>();
      map.put(this.COL_ICON, R.drawable.ic_folder);
      map.put(this.COL_PATH, "../");
      map.put(this.COL_CHECKBOX, false);
      map.put(this.COL_ABSPATH, f.getAbsolutePath());
      map.put(this.COL_MIMETYPE, "");
      this.mListMap.add(map);
    }
    Arrays.sort(files, this.mFileSortComparator_ignore_case);
    if (files != null) {
      for (int i = 0; i < files.length; i++) {
        File item = files[i];
        HashMap<String, Object> map = new HashMap<String, Object>();
        this.fileTypeClassify(map, item);
        this.mListMap.add(map);
      }
    }
    this.mSimpleAdapter = new SimpleAdapter(this, this.mListMap, R.layout.file_list_row, this.mColumnNames, this.mListRowLocations);
    this.setListAdapter(this.mSimpleAdapter);
    this.mSimpleAdapter.notifyDataSetChanged();
  }

  private void startDefaultActivityForFile(HashMap<String, Object> map, File item) {

    if (this.isPdfFile(item)){
      myUtilClass.MyTRACE("endsWith .pdf is true");
      Intent intent = new Intent(this, PdfViewActivity.class);
      intent.putExtra(parameterClass.PARA_PDF_FILE_PATH, item.getAbsolutePath());
      this.startActivity(intent);
    } else {

      Intent intent = new Intent();
      intent.setAction(android.content.Intent.ACTION_VIEW);
      File file = new File(item.getAbsolutePath());
      intent.setDataAndType(Uri.fromFile(file), map.get(this.COL_MIMETYPE).toString());
      startActivity(intent);
    }
  }
  private boolean isExternalStorageMounted() {
    boolean bret = false;
    String state = Environment.getExternalStorageState();
    if (state.equals(Environment.MEDIA_MOUNTED))
      bret = true;
    return bret;
  }
  private boolean isPdfFile(File file){
    return file.toString().endsWith(".pdf");
  }
}
